package com.neulogistics.infrastructure.ddd.domain.seedwork;

import lombok.Data;
import lombok.EqualsAndHashCode;

import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.function.BiPredicate;
import java.util.function.Predicate;
import java.util.stream.Collectors;

public abstract class Enumeration implements Comparable{
    public int id;

    public String name;

    @Override
    public String toString() {
        return name;
    }

    protected Enumeration(int id, String name){
        this.id = id;
        this.name = name;
    }

    @Override
    public boolean equals(Object o) {
        if (!(o instanceof Enumeration)) {
            return false;
        }

        boolean typeMatches = this.getClass().equals(o.getClass());
        boolean valueMatches = ((Enumeration) o).id == this.id;

        return typeMatches && valueMatches;
    }

    @Override
    public int hashCode() {
        return Objects.hash(id);
    }

    public static <T extends Enumeration> List<T> getAll(Class<T> type){
        return Arrays.stream(type.getFields())
                .filter(p -> p.getType().equals(type))
                .map(p -> {
                    Object o = null;
                    try {
                        o = p.get(null);
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                    }
                    return (T)o;
                }).collect(Collectors.toList());
    }

    public static <T extends Enumeration> T fromValue(int value, Class<T> type) {
        return parse(value, "value", type, p -> p.id == value );
    }

    public static <T extends Enumeration> T fromName(String name, Class<T> type) {
        return parse(name, "name", type, p -> p.name.equals(name));
    }

    private static <T extends Enumeration, KValue> T parse(
            KValue value, String description, Class<T> type, Predicate<T> predicate){
        Optional<T> optionalT = getAll(type).stream().filter(predicate).findFirst();
        if(optionalT.isPresent()){
            return optionalT.get();
        }
        else {
            throw new RuntimeException(
                    String.format("{%s} is not a valid %s in %s",
                            value.toString(), description, type.getName()));
            // "'{value}' is not a valid {description} in {typeof(TEnumeration)}"
        }
    }



    public static <T extends Enumeration> Boolean isValidValue(int value, Class<T> type) {
        return getAll (type).stream().anyMatch(p -> p.id == value);
    }

}
